#!/usr/bin/perl -w

########################
#                      #
# Modules	       #
#                      #
########################


use strict;  
use CGI;  											#used for collecting parameters from webbrowser
use CGI::Carp qw ( fatalsToBrowser );  								#send fatal errors to webbrowser
use Data::Dumper;										#used for debugging
use DBI;											#loads database modules

########################
#                      #
# Varibles             #
#                      #
########################

my $query = new CGI;
my $data = $query->param('data');
my $data2 = $query->param('data2');
my $sequence = $query->param('sequence');
my $xlink = $query->param('xlinkermw');
my @masses = split "\n", $data;
my @d2_masses = split "\n", $data2;
my %ms2_masses;
my $data_java;
my $data_green;
my $data_match;
my $data_red;
my $max_abundance;
my $total_ion_current;
														      

my %residuemass = (													#monoisotopic masses for rna oligo mass calculations
			g => 345.04744,
			u => 306.02530,
			c => 305.04129,
			a => 329.05252,
			G => 57.02146,											#monoisotopic masses used for calulating peptide masses
			A => 71.03711,
			S => 87.03203,											#FIXED
			P => 97.05276,
			V => 99.06841,
			T => 101.04768,
			C => 103.00919 + 57.02146, 									#103.00919 + 57.02146 carboamidemet modifications
			L => 113.08406,
			I => 113.08406,
			X => 113.08406, # (L or I)
			N => 114.04293,
			O => 114.07931,
			B => 114.53494,# (avg N+D)
			D => 115.02694,
			Q => 128.05858,
			K => 128.09496,
			Z => 128.55059,#(avg Q+E)
			E => 129.04259,
			M => 131.04049,
			H => 137.05891,
			F => 147.06841,
			R => 156.10111,
			Y => 163.06333,
			W => 186.07931
			);

my $mass_of_deuterium = 2.01410178; 											#used to calculte mass difference from linkspacing. 
my $mass_of_hydrogen = 1.00783; 
my $terminalmass=  1.0078250*2  + 15.9949146*1;
my $ms2_error = 1; 	#Dalton error for assigin an ion to a pair of ions between the two spectra
my $xlink_d = 4;
my $match_tol = 0.5;	#used for finding pairs between spectra in Daltons
my $xlink_res = 'K';
my @xlink_pos;
$xlink_pos[0] = 0;	#alpha chain xlink position
$xlink_pos[1] = 0;	#beta chain xlink position



########################
#                      #
# Main Program         #
#                      #
########################

# 
print "Content-type: text/html\n\n";
print <<ENDHTML;	
	

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>
ENDHTML

print $sequence;

print <<ENDHTML;
</title>
    <link href="/flot/layout.css" rel="stylesheet" type="text/css"></link>
    <!--[if IE]><script language="javascript" type="text/javascript" src="/flot/excanvas.min.js"></script><![endif]-->
    <script language="javascript" type="text/javascript" src="/flot/jquery.js"></script>
    <script language="javascript" type="text/javascript" src="/flot/jquery.flot.js"></script>
    <script language="javascript" type="text/javascript" src="/flot/jquery.flot.selection.js"></script>

 
<script type="text/javascript">
<!-- hide from old browsers
function getValue(varname)
{
  var url = window.location.href;
  var qparts = url.split("?");
  if (qparts.length == 0)
  {
    return "";
  }
  var query = qparts[1];
  var vars = query.split("&");
  var value = "";
  for (i=0;i<vars.length;i++)
  {
    var parts = vars[i].split("=");
    if (parts[0] == varname)
    {
      value = parts[1];
      break;
    }
  }
  value = unescape(value);
  value.replace(/\\+/g," ");
  return value;
}
// end hide -->
</script>
<style type="text/css">
body {
	background: #c7c2bc; 
	background-repeat: repeat-y;
 	background-position: center;
	font-family: Arial, Helvetica, sans-serif;	
	margin-left:auto;
	margin-right:auto;
	max-width: none;
}
div {
	padding: 0px;
}
div.white {
     background-color: white;
     text-align:left;
     margin: auto;
     width: 1024px;
     padding: 20px;
     
    
}
h1 {
    text-align: center;
    color:  #00505c;
    margin: 0;
}
div.hidden {
   display: none;
}
table {
	margin:auto;
	text-align: center;
	display: inline-block;
}
.green
{
    background-color: #50F0Fc;
}
td
{
    border-color: #600;
    text-align: left;
    margin: 0;
    padding: 10px;   
    background-color:  #d0d0d0;
}
td.table-heading
{
    border-color: #660;
    text-align: left;
    margin: 0;
    padding: 10px;   
    background-color:  #a0d0d0;
}
td.half
{
  width:50%;
}
h2
{
padding: 10px;
}
  </style>
</head>
<body>
<div class="white">
<br/><br/><h1>MS/2 -
ENDHTML

print $sequence;

print <<ENDHTML;
</h1>

<hr width="90%";>

<br/><br/>
    

    <div id="placeholder" style="margin: auto; width:800px;height:800px"></div>

  <div id="overview" style="margin: auto;width:400px;height:50px"></div>
ENDHTML

######
#
# Xlink position Guess
#
#######


my @peptides = split /-/, $sequence;
for (my $i = 0; $i < 2; $i++) {	 
  my $peptide = $peptides[$i];
  my @residues = split //, $peptide;  
  for (my $n = 0; $n  < @residues; $n++) {
    if (@residues[$n] eq $xlink_res) {
      print $i,$n,@residues[$n],$xlink_res,$peptide ;
      $xlink_pos[$i] = $n;
    last;
   }  
  }
}



#######
#
# Create DB
#
#######

my $dbh = DBI->connect("dbi:SQLite:dbname=:memory:", "", "",{ RaiseError => 1, AutoCommit => 1 });
my $masslist=  $dbh->prepare("DROP TABLE IF EXISTS masses;");
$masslist->execute();

$dbh->do("CREATE TABLE masses  (mass REAL, abundance REAL)");
my $newline = $dbh->prepare("INSERT INTO masses (mass , abundance) VALUES (?, ?)");
foreach my $mass_abundance (@masses) {
  my ($mass, $abundance) = split " ", $mass_abundance;
  if ($abundance > $max_abundance) {$max_abundance=$abundance};
  $ms2_masses{$mass}=$abundance;
  $newline->execute($mass,$abundance);
  $total_ion_current = $total_ion_current + $abundance;
  $data_java = $data_java. '['.$mass.','.$abundance.'],';
}

$dbh->do("CREATE TABLE d2_masses  (mass REAL, abundance REAL)");
my $newline = $dbh->prepare("INSERT INTO d2_masses (mass, abundance) VALUES (?, ?)");
foreach my $mass_abundance (@d2_masses)	{
  my ($mass, $abundance) = split " ", $mass_abundance;
  if ($abundance > $max_abundance) {$max_abundance=$abundance};
  $newline->execute($mass,$abundance);
  $data_java = $data_java. '['.$mass.','.-$abundance.'],';
}        

my $mass_seperation_upper = +$ms2_error;
my $mass_seperation_lower = -$ms2_error;
my $matchlist = $dbh->prepare("SELECT masses.*, d2_masses.mass as d2_mass, d2_masses.abundance 
    as d2_abundance FROM masses inner join d2_masses on (d2_masses.mass between masses.mass -  ? and masses.mass + ?)");
my %matched_common;
my %d2_matched_common;
my %matched_xlink;
$matchlist->execute($match_tol,$match_tol);
while (my $searchmass = $matchlist->fetchrow_hashref) {
  $matched_common{$searchmass->{'mass'}} = $searchmass->{'abundance'};
  $d2_matched_common{$searchmass->{'d2_mass'}} = $searchmass->{'d2_abundance'};
  $data_green = $data_green . '['.$searchmass->{'mass'}.','.$searchmass->{'abundance'}.'],';
  $data_green = $data_green . '['.$searchmass->{'d2_mass'}.','.-$searchmass->{'d2_abundance'}.'],';
}	

my $matchlist = $dbh->prepare("SELECT masses.*, d2_masses.mass as d2_mass, d2_masses.abundance 
    as d2_abundance FROM masses inner join d2_masses on (d2_masses.mass between masses.mass +  (?) and masses.mass + (?))");
for (my $i = 1; $i<4; $i++)  {
  $matchlist->execute(($xlink_d/$i)- $match_tol, ($xlink_d/$i)+$match_tol);
  while (my $searchmass = $matchlist->fetchrow_hashref) {
    $matched_xlink{$searchmass->{'mass'}} = $searchmass->{'abundance'};
#     if (defined $matched_common{$searchmass->{'mass'}} == 0 && defined $d2_matched_common{$searchmass->{'d2_mass'}} == 0) { 	#Purely for cosmetic reasons don't add overlapping peaks
      $data_red = $data_red . '['.$searchmass->{'mass'}.','.$searchmass->{'abundance'}.'],';
      $data_red = $data_red . '['.$searchmass->{'d2_mass'}.','.-$searchmass->{'d2_abundance'}.'],';
#     }
  }
}					


my @peptides = split /-/, $sequence;
my @xlink_half;
for (my $i = 0; $i < 2; $i++) {	 
  my $peptide = $peptides[$i];
  my @residues = split //, $peptide;
  my $peptide_mass = 0;
  foreach my $residue (@residues) {												#split the peptide in indivual amino acids
      $peptide_mass = $peptide_mass + $residuemass{$residue};									#tally the masses of each amino acid one at a time
    }
    $xlink_half[$i] = $peptide_mass+$terminalmass;
}


for (my $i = 0; $i < @peptides; $i++) {	 
  if ($i == 0) {print "<h2>alpha-chain</h2>";} else {print "<h2>beta-chain</h2>";}
  my $peptide = $peptides[$i];
  print "<br/><br/><table border=0 cellpadding=4><tr>";
  for (my $n = 0; $n < @peptides; $n++) {
    for (my $charge = 1; $charge < 4; $charge++) {
      print "<td class='table-heading'>";
      if ($n > 0) {print " xlink-";}
      print "b-ion+$charge</td>";
      my @residues = split //, $peptide;
      my $ion_mass = 0;
      my $residue_no = 0;
      foreach my $residue (@residues) {	
	$residue_no = $residue_no +1;
	print "<td";
	$ion_mass = $ion_mass + $residuemass{$residue};
	my $mz = (($ion_mass+($charge*$mass_of_hydrogen)+($n*($xlink+$xlink_half[abs($i-1)])))/$charge);
	my $match = 0;
	if ($n == 0) {
	  foreach my $ms2_mass (keys %matched_common) {
	    if ($ms2_mass < ($mz +$ms2_error) && $ms2_mass > ($mz - $ms2_error)) {
	      if (abs($match - $mz) > abs($mz - $ms2_mass)) {$match = $ms2_mass};
	    }
	  }
	} else {
	  foreach my $ms2_mass (keys %matched_xlink) {
	    if ($ms2_mass < ($mz +$ms2_error) && $ms2_mass > ($mz - $ms2_error)) {
	      if (abs($match - $mz) > abs($mz - $ms2_mass)) {$match = $ms2_mass};
	    }
	  }
	}
	
	  if ($n == 0 && $residue_no <= $xlink_pos[$i] && $match != 0 ) {
	    $data_match = $data_match . '['. ($match) .','.$matched_common{$match} .'],';
	    if ($matched_common{$match}/$max_abundance < 0.001)
	      { print " style='background-color:#008800' >"; } 
	    elsif ($matched_common{$match}/$max_abundance < 0.01)
	      {print " style='background-color:#00AA00' >";}
	    elsif ($matched_common{$match}/$max_abundance < 0.1)
	      {print " style='background-color:#00DD00' >";}
	    else
	      {print " style='background-color:#00FF00' >";}
	    } 
	    elsif ($n == 1 && $residue_no > $xlink_pos[$i] && $match != 0){
	      $data_match = $data_match . '['. ($match) .','.$matched_xlink{$match} .'],';
	      if ($matched_xlink{$match}/$max_abundance < 0.001)
	      { print " style='background-color:#880000' >"; } 
	    elsif ($matched_xlink{$match}/$max_abundance < 0.01)
	      {print " style='background-color:#AA0000' >";}
	    elsif ($matched_xlink{$match}/$max_abundance < 0.1)
	      {print " style='background-color:#DD0000' >";}
	    else
	      {print " style='background-color:#FF0000' >";}
	    }
	  elsif ($residue_no > $xlink_pos[$i] && $n==0) {
	    print " style='background-color:#F0F0F0; color:#D0D0D0;' >";
	  } elsif ($residue_no <= $xlink_pos[$i] && $n==1) {
	    print " style='background-color:#F0F0F0; color:#D0D0D0;' >";
	  } else  {
	    print ">";	
	  }
	printf "<B>%.2f</B>", $mz;;
	if ($match !=0 ) {printf "<br/>(%.2f)", $match;};
	print "</td>";
      }
    print "</tr>\n<tr>";
    }
  }



  print "<td class='table-heading'>AA</td>";
  my @residues = split //, $peptide;
    foreach my $residue (@residues) {	
      print "<td class='table-heading'>$residue</td>";
     }
 print "</tr>\n<tr>";

  for (my $n = 0; $n < @peptides; $n++)
  {
  
  for (my $charge = 1; $charge < 4; $charge++)
    {
    print "<td class='table-heading'>";
    if ($n > 0) {print " xlink-";}
    print "y-ion+$charge</td><td style='background-color:#F0F0F0; color:#D0D0D0;'></td>";
    my @residues = split //, $peptide;
    my $peptide_mass = 0;
    foreach my $residue (@residues) {									#split the peptide in indivual amino acids
	    $peptide_mass = $peptide_mass + $residuemass{$residue};					#tally the masses of each amino acid one at a time
	  }
      my $ion_mass = $peptide_mass;
     my $residue_no = 0;
      foreach my $residue (@residues) {	
	$residue_no = $residue_no +1;
	if ($residue_no == @residues) { last;}
	print "<td";
	$ion_mass = $ion_mass - $residuemass{$residue};
	my $mz = (($ion_mass+$terminalmass+($charge*$mass_of_hydrogen)+($n*($xlink+$xlink_half[abs($i-1)])))/$charge);
	my $match = 0;
	if ($n == 0) {
	  foreach my $ms2_mass (keys %matched_common)
	  {
	  if ($ms2_mass < ($mz +$ms2_error) && $ms2_mass > ($mz - $ms2_error)) 
	      {
	      if (abs($match - $mz) > abs($mz - $ms2_mass)) {$match = $ms2_mass};
	      }
	  }
	} else {
	  foreach my $ms2_mass (keys %matched_xlink)
	  {
	    if ($ms2_mass < ($mz +$ms2_error) && $ms2_mass > ($mz - $ms2_error)) 
	    {
	      if (abs($match - $mz) > abs($mz - $ms2_mass)) {$match = $ms2_mass};
	    }
	  }
	}
	 if ($n == 0 && $residue_no-1 >= $xlink_pos[$i] && $match != 0 ) {
	    $data_match = $data_match . '['. ($match) . ','.$matched_common{$match} .'],';
	    if ($matched_common{$match}/$max_abundance < 0.001)
	      { print " style='background-color:#008800' >"; } 
	    elsif ($matched_common{$match}/$max_abundance < 0.01)
	      {print " style='background-color:#00AA00' >";}
	    elsif ($matched_common{$match}/$max_abundance < 0.1)
	      {print " style='background-color:#00DD00' >";}
	    else
	      {print " style='background-color:#00FF00' >";}
	    } 
	    elsif ($n == 1 && $residue_no-1 < $xlink_pos[$i] && $match != 0){
	      $data_match = $data_match . '['. ($match) .' ,'.$matched_xlink{$match} .'],';
	      if ($matched_xlink{$match}/$max_abundance < 0.001)
		{ print " style='background-color:#880000' >"; } 
	      elsif ($matched_xlink{$match}/$max_abundance < 0.01)
		{print " style='background-color:#AA0000' >";}
	      elsif ($matched_xlink{$match}/$max_abundance < 0.1)
		{print " style='background-color:#DD0000' >";}
	      else
		{print " style='background-color:#FF0000' >";}
	    }
	  elsif ($residue_no-1 < $xlink_pos[$i] && $n==0) {
	    print " style='background-color:#F0F0F0; color:#D0D0D0;' >";
	  } elsif ($residue_no-1 >= $xlink_pos[$i] && $n==1) {
	    print " style='background-color:#F0F0F0; color:#D0D0D0;' >";
	  } else  {
	    print ">";	
	  }
	printf "<B>%.2f</B>", $mz;;
	if ($match !=0 ) {printf "<br/>(%.2f)", $match;};
	print "</td>";
      }
  print "</tr>\n<tr>";
  }
  }


  print "</tr></table>";
}

print <<ENDHTML;
<script id="source" language="javascript" type="text/javascript">
\$(function () {

ENDHTML

print "  var sin = [";
print $data_java;
print "];\n";

print "  var match = [";
print $data_match;
print "];\n";

print "  var green = [";
print $data_green;
print "];\n";

print "  var red = [";
print $data_red;
print "];\n";


print <<ENDHTML        




    var options = {
 
               series: {
 		   bars: {show: true, lineWidth: 0.75},
 		   shadowSize: 0    

                },
		selection: { mode: "x" },
                grid: { hoverable: true, clickable: false },
    };


     var plot = \$.plot(\$("#placeholder"),
            [ { data: sin, color: '#d0d0d0'}, {data:red,  color: '#ff0000'},{data:green, color: '#00FF00'}, {data:match,points: {show: true}, bars: {show: false}, color: '#000000'} ], options);
 
     function showTooltip(x, y, contents) {
         \$('<div id="tooltip">' + contents + '</div>').css( {
             position: 'absolute',
             display: 'none',
             top: y + 5,
             left: x + 5,
             border: '1px solid #fdd',
             padding: '2px',
             'background-color': '#fee',
             opacity: 0.80
         }).appendTo("body").fadeIn(200);
     }
 
     var previousPoint = null;
     \$("#placeholder").bind("plothover", function (event, pos, item) {
         \$("#x").text(pos.x.toFixed(2));
         \$("#y").text(pos.y.toFixed(2));
 
         
             if (item) {
                 if (previousPoint != item.datapoint) {
                     previousPoint = item.datapoint;
                     
                     \$("#tooltip").remove();
                     var x = item.datapoint[0].toFixed(2),
                         y = item.datapoint[1].toFixed(2);
                     
                     showTooltip(item.pageX, item.pageY, x);
                 }
             
             else {
                \$("#tooltip").remove();
                 previousPoint = null;            
             }
         }
     });
	




 var overview = \$.plot(\$("#overview"), [{ data: sin, color: '#d0d0d0'}, {data:red,  color: '#ff0000'},{data:green,  color: '#00FF00'}], {
        series: {
            bars: { show: true, lineWidth: 1 },
	    
            shadowSize: 0
        },
	
        xaxis: { ticks: []},
        yaxis: { ticks: [], min: 0, autoscaleMargin: 0.1 },
        selection: { mode: "x" }
    });

    // now connect the two
    
    \$("#placeholder").bind("plotselected", function (event, ranges) {
        // do the zooming
        plot = \$.plot(\$("#placeholder"), [{ data: sin, color: '#d0d0d0'},{data:red,  color: '#ff0000'}, {data:green, color: '#00FF00'},{data:match,points: {show: true}, bars: {show: false}, color: '#000000'} ],
                      \$.extend(true, {}, options, {
                          xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
                      }));

        // don't fire event on the overview to prevent eternal loop
        overview.setSelection(ranges, true);
    });
    
    \$("#overview").bind("plotselected", function (event, ranges) {
        plot.setSelection(ranges);
    });


    
   
   
});
</script>
<br/><br/>
</div>

 </body>
</html>

ENDHTML


